囉唆一下:因為我要出去玩ㄦ,今天的文章先抄一下UnicodeError雜談所提到的一些版本差異小知識,祝大家今年都過的沒有遺憾,快快樂樂迎新年~
昨天講了關於python2的print語句與python3的print函數的差別,今天來探討一下關於這兩個版本對於字串處理有何差別,這可是不小的議題,需要好好探究一下。
(1) 類型命名
之前在UnicodeError的議題中有提過,python把其他編碼字串和unicode編碼字串視為不同類型的字串物件。而python2和python3對這兩種物件類型又有不一樣的命名方式:
------- | unicode | other |
---|---|---|
python2 | unicode | str |
python3 | str | bytes |
為何python3要更改物件的命名呢?這是因為python3的命名比較切合實際的意義,也比較不會讓開發者常常搞混,我們可以這樣去理解python3的命名:
因為每個不同的字符都有他獨立的unicode編碼,如同不同的字在我們眼中看起來都是不一樣的,易於區別,所以在python3將其認定為真正的'str'物件,而其他種編碼方式雖不一定能給予每個字不同的識別碼,但相較於unicode,其他編碼方式有比較小的容量,因此比較容易傳遞並儲存,這個特性就像我們在存取或傳遞文字、聲音、影像時,都會以一串由1和0組成的位元組來進行,所以在python3會把其他編碼類型的字串物件視為'Bytes'物件。
但想要從python2轉到python3的開發者們,要記得'str'這個類型名稱在兩種版本裡所指的物件是完全相反的,這點不要搞混。
在下面的討論中,我將會比照python3的命名,以str物件稱呼unicode編碼字串,並以Bytes物件稱呼其他編碼字串。
(2) 對於字串常數的處理
假設我們現在要對一個變數example賦值,而這個值的內容為'範例',這很簡單,只要一行指令example='範例'就行了,但現在遇到一個問題,那這個example究竟是str物件還是Bytes物件呢?
一般來說,筆者會希望這一個str物件,為什麼呢?因為若是Bytes物件,那通常作業系統給予其編碼型式為'utf-8',但是'utf-8'卻是一個可變長度的編碼型式,對於一般的英文字母,位元組的長度為1Bytes,但大部份中文卻有3Bytes,那如果我要使用len(example)計算長度,那就是勢必會出現非預期的狀況:
In python2 shell:
>>> example='範例' # python2不會自動解碼字串常數,所以目前example是一個utf-8編碼的Bytes物件
>>> print(len(example))
6
>>> for c in example:
... print c
...
�
�
�
�
>>>
In python3 shell:
>>> print(len(example))
2
>>> for c in example:
... print(c)
...
範
例
>>>
在python2中,保留了原始的Bytes字串的後果,就是python無法將其視為一個完整的字做處理,以至於無法用一些很直觀的方式來操作字串,有鑑於此,開發者只能細心的在賦值時加上u前綴: example=u'範例',python3意識到了這種問題,所以對於字串常數,會自動將其解碼為str物件,讓開發者不會在這裡採坑。
如果想要在python2使用python3的字串常數處理方法,可以使用from __future__ import unicode_literals。
(3) 混合類型的相加
這個在UnicodeError雜談有提及,對於str字串和Bytes字串的相加,在python3中這是被禁止的,python會拋出TypeError,但是在python2中,會預設先把byte字串用ascii解碼,再做相加的動作,這在一般的英文字母中是行的通的:
In python2 shell:
>>> a='example1' # byte字串
>>> b=u'example2' # str字串
>>> c=a+b
>>> print c
example1example2
>>> print type(c) # 因為a已經是先被解碼再做相加,所以c是一個str字串
<type 'unicode'>
>>>
但如果要解碼的byte字串無法用ascii解碼,就會出現UnicodeDecodeError:
In python2 shell:
>>> a='範例1'
>>> b=u'範例1'
>>> c=a+b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 0: ordinal not in range(128)
>>>
似乎字串處理還有一些東西沒有講,但今天就先讓大家頭腦休息一下,明天再接再勵,我也再接再勵。